﻿using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Domain.Repositories;
using pandx.Mulan.Books.Dto;
using Abp.Linq.Extensions;
using System.Linq;
using Abp.Authorization;
using Abp.Extensions;
using Microsoft.EntityFrameworkCore;
using pandx.Mulan.Authorization;
using pandx.Mulan.Authorization.Users;
using pandx.Mulan.Dto;
using pandx.Mulan.Books.Exporting;

namespace pandx.Mulan.Books
{
    public class BookAppService:MulanAppServiceBase,IBookAppService
    {
        private readonly IRepository<Book, long> _bookRepository;
        private readonly IRepository<User, long> _userRepository;
        private readonly IBookListExcelExporter _bookListExcelExporter;

        public BookAppService(
            IRepository<Book, long> bookRepository,
            IRepository<User,long> userRepository,
            IBookListExcelExporter bookListExcelExporter
            )
        {
            _userRepository = userRepository;
            _bookRepository = bookRepository;
            _bookListExcelExporter = bookListExcelExporter;
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Browse)]
        public async Task<PagedResultDto<BookListDto>> ObtainBooks(ObtainBooksInput input)
        {

            var query = CreateBookAndUsersQuery(input);

            var booksCount = await query.CountAsync();
            var books = await query.OrderBy(input.Sorting).PageBy(input).ToListAsync();
            var dtos = books.Select(item =>
            {
                var dto = ObjectMapper.Map<BookListDto>(item.Book);
                dto.CreatorUserName = item.User.UserName;
                dto.CreatorName = item.User.Name;
                return dto;
            }).ToList();
            return new PagedResultDto<BookListDto>(booksCount, dtos);
        }

        public async Task CreateOrUpdateBook(CreateOrUpdateBookInput input)
        {
            if (input.Book.Id.HasValue)
            {
                await UpdateBookAsync(input);
            }
            else
            {
                await CreateBookAsync(input);
            }
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Update)]
        protected virtual async Task UpdateBookAsync(CreateOrUpdateBookInput input)
        {
            var book = await _bookRepository.GetAsync(input.Book.Id.Value);
            ObjectMapper.Map(input.Book, book);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Create)]
        protected virtual async Task CreateBookAsync(CreateOrUpdateBookInput input)
        {
            if (AbpSession.TenantId.HasValue)
            {
            }
            var book = ObjectMapper.Map<Book>(input.Book);

            book.TenantId=AbpSession.TenantId;

            await _bookRepository.InsertAsync(book);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Create,AppPermissions.Pages_Books_Update)]
        public async Task<GetBookForEditOutput> GetBookForEdit(NullableIdDto<long> input)
        {
            var output = new GetBookForEditOutput();
            if (input.Id.HasValue)
            {
                var book = await _bookRepository.GetAsync(input.Id.Value);
                output.Book = ObjectMapper.Map<BookEditDto>(book);
            }
            else
            {
                var book = new BookEditDto();
                output.Book = book;
            }

            return output;
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Browse)]
        public async Task<BookListDto> GetBook(EntityDto<long> input)
        {
            var book = await _bookRepository.GetAsync(input.Id);
            return ObjectMapper.Map<BookListDto>(book);
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Delete)]
        public async Task DeleteBook(EntityDto<long> input)
        {
            var book = await _bookRepository.GetAsync(input.Id);
            await _bookRepository.DeleteAsync(input.Id);
        }

        [AbpAuthorize(AppPermissions.Pages_Books_Export)]
        public async Task<FileDto> ObtainBooksToExcel(ObtainBooksInput input)
        {
            var books = await CreateBookAndUsersQuery(input).AsNoTracking().OrderBy(input.Sorting).ToListAsync();
            var dtos=books.Select(item =>
            {
                var dto = ObjectMapper.Map<BookListDto>(item.Book);
                dto.CreatorUserName = item.User.UserName;
                dto.CreatorName = item.User.Name;
                return dto;
            }).ToList();
            return _bookListExcelExporter.ExportToExcel(dtos);
        }
        private IQueryable<BookAndUser> CreateBookAndUsersQuery(ObtainBooksInput input)
        {
            var query = from book in _bookRepository.GetAll()
                join user in _userRepository.GetAll() on book.CreatorUserId equals user.Id into userJoin
                from joinedUser in userJoin.DefaultIfEmpty()
                where book.CreationTime >= input.StartTime && book.CreationTime <= input.EndTime
                select new BookAndUser
                {
                    Book = book,
                    User=joinedUser
                };
            query = query
                .WhereIf(!input.Filter.IsNullOrWhiteSpace(), item => item.User.UserName.Contains(input.Filter) ||item.User.Name.Contains(input.Filter));
            return query;
        }
        
        [AbpAuthorize(AppPermissions.Pages_Books_Create,AppPermissions.Pages_Books_Update)]
        public async Task<ValidateOutput> ValidateTitle(ValidateInput<string, int> input)
        {
            if (input.Id == 0)
            {
                var one = await _bookRepository.GetAll().SingleOrDefaultAsync(r => r.Title == input.Value);
                if (one != null)
                {
                    return new ValidateOutput
                    {
                        Status = false,
                        Message = "题名 " + input.Value + " 已被占用"
                    };
                }
                else
                {
                    return new ValidateOutput
                    {
                        Status = true,
                    };
                }
            }
            else
            {
                var one = await _bookRepository.GetAll().SingleOrDefaultAsync(r =>
                    r.Title == input.Value && r.Id != input.Id);
                if (one != null)
                {
                    return new ValidateOutput
                    {
                        Status = false,
                        Message = "题名 " + input.Value + " 已被占用"
                    };
                }
                else
                {
                    return new ValidateOutput
                    {
                        Status = true,
                    };
                }
            }
        }
    }
}
